package com.mf.service.impl;

import com.alibaba.fastjson2.JSON;
import com.mf.service.dto.WebSocketDto;
import io.github.heollhai.common.entity.request.SocketMessageRequest;
import io.github.heollhai.common.enums.EnumSocketType;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: lmf
 * @Create: 2024/9/23 15:13
 * Description:
 */

@Component
@Slf4j
//此注解相当于设置访问URL
@ServerEndpoint("/basic/websocket/{id}")
public class WebSocketServiceImpl {


    // 静态变量，用来记录当前对应的websocket连接
    public static Map<String, WebSocketDto> webSockets = new ConcurrentHashMap<>();

    // 静态变量，按照用户id来记录，每个用户的多个session
    public static Map<String, List<WebSocketDto>> webSocketByUserIdMap = new ConcurrentHashMap<>();

    private Session session;

    private static final ReentrantLock lock = new ReentrantLock();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("id") String userId) {
        log.info("连接成功: {}=================================================<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
                userId);

        this.session = session;
        WebSocketDto webSocketDto = WebSocketDto.builder()
                .sessionId(session.getId())
                .userId(userId)
                .webSocketServiceImpl(this)
                .session(session)
                .build();

        webSockets.put(session.getId(), webSocketDto);

        // 为同一个账号的多个session，保存起来后续如果需要对该账号进行消息推送可以快速找到
        List<WebSocketDto> webSocketDtos = webSocketByUserIdMap.computeIfAbsent(userId,
                k -> new CopyOnWriteArrayList<>());
        webSocketDtos.add(webSocketDto);



        log.info("【websocket消息】有新的连接，总数为: {}", webSockets.size());
    }



    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        String id = this.session.getId();

        // 移除用户下对应的会话
        WebSocketDto webSocketDto = webSockets.get(id);
        List<WebSocketDto> webSocketDtos = webSocketByUserIdMap.get(webSocketDto.getUserId());
        log.info("移除前webSocketDtos:{}", webSocketDtos);
        if (webSocketDtos != null) {
            webSocketDtos.remove(webSocketDto);
            if (webSocketDtos.isEmpty()) {
                webSocketByUserIdMap.remove(webSocketDto.getUserId());
            }
        }


        log.info("移除后webSocketDtos=============》》》》:{}", webSocketDtos);
        log.info("webSocketByUserIdMap===>>:{}", webSocketByUserIdMap);

        webSockets.remove(id);
        log.info("session断开id:{}", id);
        log.info("【websocket消息】连接断开==>，总数为: {}", webSockets.size());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {

        // 心跳包
        if (message.contains("ping")) {
            log.info("收到客户端心跳包:{}",session.getId()+session.getRequestURI());
            final SocketMessageRequest messageRequest = new SocketMessageRequest();
            messageRequest.setMessage("back-message" + session.getId());
            messageRequest.setType(EnumSocketType.SYSTEM);
            sendTextMessage(session.getId(), messageRequest);
            return;
        }

        log.info("webSocketByUserIdMap:{}", webSocketByUserIdMap);

        // 发送消息给客户端

//        sendAllMessage(session.getId() + "============>>>>>> 这个是群发消息" + message + session.getId());
//
//        sendTextMessageByUserId("6", "群发的消息只给对应用户发送消息");
//        log.info("【websocket消息】发送消息，总数为: {}", webSockets.size());
    }

    @OnError
    public void onError(Throwable error) {
        log.error("【websocket消息】发生错误: {}", error.getMessage(), error);
    }

    /**
     * 发送单个会话的消息 暂时没有需要用到这种的方式
     *
     * @param sessionId 会话ID
     * @param messageRequest   消息内容
     */
    public void sendTextMessage(String sessionId, SocketMessageRequest messageRequest) {
        lock.lock();
        try {
            WebSocketDto webSocketDto = webSockets.get(sessionId);
            if (webSocketDto == null || !webSocketDto.getSession().isOpen()) {
                log.warn("【websocket消息】会话无效或已关闭，无法发送消息: {}", sessionId);
                return;
            }

            try {
                webSocketDto.getSession().getBasicRemote().sendText(JSON.toJSONString(messageRequest));
            } catch (Exception e) {
                log.error("【websocket消息】发送消息失败: {}", e.getMessage(), e);
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 给指定用户的所有会话发送消息
     */
    public static void sendTextMessageByUserId(SocketMessageRequest socketMessageRequest) {
        lock.lock();
        try {
            List<WebSocketDto> webSocketDtos = webSocketByUserIdMap.get(socketMessageRequest.getUserId());
            sendTextMessageByUserId(webSocketDtos, socketMessageRequest);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 给指定用户id下所有会话发送消息
     */
    public static void sendTextMessageByUserId(List<WebSocketDto> webSocketDtos,SocketMessageRequest socketMessageRequest) {
        if (webSocketDtos == null) {
            log.warn("【websocket消息】用户不存在，无法发送消息: {}", socketMessageRequest.getUserId());
            return;
        }
        for (WebSocketDto webSocketDto : webSocketDtos) {
            Session sess = webSocketDto.getSession();
            if (sess != null && sess.isOpen()) {
                try {
                    sess.getBasicRemote().sendText(JSON.toJSONString(socketMessageRequest));
                } catch (Exception e) {
                    log.error("【websocket消息】发送消息失败: {}", e.getMessage(), e);
                }
            } else {
                log.warn("【websocket消息】会话无效或已关闭，无法发送消息: {}", webSocketDto.getSessionId());
            }
        }
    }

    /**
     * 广播消息给所有会话
     *
     * @param socketMessageRequest 消息内容
     */
    public static void sendAllMessage(SocketMessageRequest socketMessageRequest) {
        lock.lock();
        try {
            log.info("【websocket消息】开始广播消息: {}", webSockets.size());

            for (Map.Entry<String, WebSocketDto> entry : webSockets.entrySet()) {
                WebSocketDto webSocketDto = entry.getValue();
                Session sess = webSocketDto.getSession();
                if (sess != null && sess.isOpen()) {
                    try {
                        sess.getBasicRemote().sendText(JSON.toJSONString(socketMessageRequest));
                    } catch (Exception e) {
                        log.error("【websocket消息】发送消息失败: {}", e.getMessage(), e);
                    }
                } else {
                    log.warn("【websocket消息】会话无效或已关闭，无法发送消息: {}", entry.getKey());
                }
            }
        } finally {
            lock.unlock();
        }

        log.info("【websocket消息】广播消息结束");
    }
}

